%include "gdt.inc"
org	0100h

jmp LABEL_START
BaseOfStack equ 0100h
BaseOfKernel equ 08000h
BaseOfLoader equ 09000h
KernelEntry  equ 030400h
OffsetOfKernel equ 0h
StartDir	equ	19  ;目录项开始扇区
SizeDir		equ	14  ;目录项总共扇区数目
_SizeDir	db 	0   ;
_StartDir	db	0   ;
StarFat		equ	1   ;FAT表开始扇区
SizeFat	equ	9	    ;FAT表总共扇区数目
SecPerTrk	equ	18  ;每磁道扇区数
BPB_BytsPerSec	equ	512
FileName db "KERNEL  O  ",0   ;要查找的文件名
Index	db	0
index  	dw	0
RootDirSectors equ 31
BLOCK 	db	2
MAXFATIndex  equ 	9	;FAT最大扇区数
MAXIndex	equ	32	;1k中最大目录结构
StartFat	equ	1
_SizeFat    	db	0
_Flag db 0
FirstClus dw 0
MemSize  dd 0
ARDSLen   dd 0
FileSize equ	11	    ;文件名大小
BasePhyAddr equ BaseOfLoader*10h
KernelAddr	equ	BaseOfKernel*16
MemBuf: times 512 db 0
BaseStack:times 512 db 0
[section .gdt]
LABEL_GDT:	Descriptor 0,0,0
LABEL_CODE:	Descriptor 0,0ffffffh,DA_32+DA_CR+DA_LIMIT_4K
LABEL_DATA:	Descriptor 0,0ffffffh,DA_32+DA_DRW+DA_LIMIT_4K
LABEL_VIDEO:	Descriptor 0B8000h,0ffffh,DA_DRW+DA_DPL3
GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen -1
	dd BasePhyAddr+LABEL_GDT

SelectorCode equ  LABEL_CODE - LABEL_GDT
SelectorData equ  LABEL_DATA - LABEL_GDT
SelectorVideo equ LABEL_VIDEO - LABEL_GDT
LABEL_START:
	mov ax,cs
	mov ds,ax
	mov es,ax
	mov ss,ax
	mov sp,BaseOfStack
	
	;复位软驱
	xor ax,ax
	mov ah,0
	int 13h
	
	mov al,0h
	mov bh,0ch
	mov cx,0
	mov cl,0
	mov dh,48
	mov dl,50
	mov ah,6
	int 10h
	mov ah,2h
	mov bh,0h
	mov dx,0
	int 10h
	
	;开始读取kernel.bin文件
LABEL_SEARCH:
	mov byte[_SizeDir],SizeDir
	mov byte[_StartDir],StartDir 
	cmp byte[_SizeDir],0
	jz LABEL_OVER
	mov ax,BaseOfKernel	;加载段选择子 只能用ax
	mov es,ax
	mov bx,OffsetOfKernel
	xor ax,ax
	mov al,byte[_StartDir]
	push ax
	call LABEL_READ
	add sp,2
LABEL_FOR:
	mov al,byte[Index]
	cmp al,MAXIndex
	jz LABEL_OVER
	push FileSize		;压入文件名大小
	call LABEL_CMP		;调用字符串比较函数
	add sp,2
	cmp ax,0		;返回比较结果
	jz LABEL_FIND
	add bx,32		;每个目录项的大小为32byte
	inc byte[Index]		;增加index
	jmp LABEL_FOR
	xor eax,eax
	add byte[_StartDir],2
	mov al,byte[_StartDir]
	sub byte[_SizeDir],2
	jmp LABEL_SEARCH
LABEL_OVER:
	jmp 	$
	nop
	nop
LABEL_FIND:
	xor di,di
	xor ax,ax
	mov di,bx
	add di,01Ah
	mov ax,word[es:di]
	mov word[FirstClus],ax
	push ax
	call LABEL_LOADE_FILE
	add sp,2
LABEL_FAT:
	mov byte[_SizeFat],SizeFat
.3:	cmp byte[_SizeFat],0
	jz  LABEL_OVER	
	mov ax,BaseOfKernel
	sub ax,0100h        ;用于存放FAT
	mov es,ax
	mov bx,OffsetOfKernel
	xor ax,ax
	mov ax,word[FirstClus] ;簇编号
	call LABEL_GETFAT
	cmp ax,0FFFh
	jz LABEL_GO
	mov word[FirstClus],ax
	push ax
	call LABEL_LOADE_FILE
	add sp,2
	mov ax,word[FirstClus] ;for debug 
	sub byte[_SizeFat],2
	jmp .3
LABEL_GETFAT:
	xor dx,dx
	xor bx,bx
	mov bx,3
	mul bx   	;ax=ax*3
	xor bx,bx
	mov bx,2
	div bx		;ax/2 =>商在ax,余数在dx
	
	push ax
	push dx
	xor dx,dx
	mov bx,BPB_BytsPerSec 		;每簇字节数
	div bx			;ax为相对扇区
	add ax,StartFat
	mov bx,OffsetOfKernel
	push ax
	mov byte[BLOCK],2
	call LABEL_READ
	add sp,2	;实模式下
	mov byte[_Flag],0
	xor dx,dx
	pop dx
	cmp dx,0
	jz LABEL_ODD
	mov byte[_Flag],1
LABEL_ODD:
	pop ax
	xor bx,bx
	add bx,ax
	xor ax,ax
	mov ax,[es:bx]
	cmp byte[_Flag],1
	jnz LABEL_EVEN
	shr ax,4
	ret
LABEL_EVEN: ;偶数
	and ax,0fffh
	ret
LABEL_GO:
	mov ax,ds
	mov es,ax ;重置es值
	;中断15 获取内存大小
	mov di,MemBuf
	mov ebx,0
	xor eax,eax
.1:
	mov ax,0E820h
	mov ecx,20
	mov edx,0534D4150h
	int 15h
	jc .2
	add di,20
	inc dword[ARDSLen]
	cmp ebx,0
	jne .1	
.2:
	xor	esi,esi
	xor	ecx,ecx
	mov	esi,MemBuf
	mov	ecx,dword[ARDSLen]
;---------------------------------------------------
;	地址范围描述符结构
;		BaseAddrLow	dd
;		BaseAddrHigh	dd
;		LengthLow	dd
;		LengthHigh	dd
;		Type		dd
;----------------------------------------------------	
.3:
	push	esi
	cmp	dword[esi+16],1
	jne	.4	
	mov	eax,dword[esi]
	add	eax,dword[esi+8]
	cmp	eax,dword[MemSize]
	jb	.4
	mov	dword[MemSize],eax
.4:
	pop	esi
	add	esi,20
	loop	.3

	lgdt [GdtPtr]
	cli
	in al,92h
	or al,02h
	out 92h,al
	mov eax,cr0
	or eax,1
	mov cr0,eax
	jmp	dword 08h:(BasePhyAddr+start)
[section .s32]
[bits 32]
start:
	mov	ax,SelectorData
	mov	ds,ax
	mov	es,ax
	mov	ss,ax
	mov	esp,BaseStack
	xor	ecx,ecx
	mov	cx,word[KernelAddr+44] ;ELF->e_phnum 共有多少代码段
	xor	eax,eax
	mov     eax,dword[KernelAddr+28] ;ELF->e_phoff 相对于文件的偏移值
	add	eax,KernelAddr ;代码段所在的地址
.8:
	push	ecx
	mov	esi,dword[eax+4]	;ELF->p_offset 代码所在文件位置
	add	esi,KernelAddr

	mov	edi,dword[eax+8] ;ELF->p_vaddr 代码段想在的地址
	mov 	ecx,dword[eax+16] ;ELF->p_filesz 代码段的长度
	cld
	rep	movsb	;复制数据
	pop	ecx
	add	eax,32		;下一个programming 段
	loop	.8
	cli
	push	KernelEntry
	ret
	nop
	xor	edi,edi
	mov	edi,100000h
	mov	ecx,1024
	xor	eax,eax
	mov	eax,101000h+PG_P+PG_U+PG_RW
	cld
.5:
	stosd	
	add	eax,1024*4 ;单个页表为是4K
	loop	.5

	;注意 div
	;被除数		商	余数
	;EDX:EAX	EAX	EDX	;64位
	;AX		AL	AH	；16
	;DX:AX		AX	DX	;32
	xor	edx,edx
	xor	eax,eax
	mov	ebx,1024*4	;一页的大小4k
	mov	eax,dword[BasePhyAddr+MemSize]
	div	ebx
	;总是最小满足
	mov	ecx,eax ;求得需要的页数
	xor	eax,eax
	xor	edi,edi
	mov	edi,101000h
	mov	eax,PG_P+PG_U+PG_RW
	cld
.6:
	stosd
	add	eax,1024*4
	loop	.6
	
	mov	eax,0
	mov	cr3,eax
	xor	eax,eax
	cli
	mov	eax,cr0
	or	eax,80000000h
	mov	cr0,eax
	xor	eax,eax
	jmp	short .7
.7:
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	push	KernelEntry
	nop
	ret
[section  .s32]
[bits 32]
Start:
	mov	ax,SelectorVideo
	mov	gs,ax
	mov	edi,(80*12+0)*2
	mov	ah,0ch
	mov	al,'K'
	mov	[gs:edi],ax
	jmp	$
[section .s16]
[bits 16]
LABEL_LOADE_FILE:
	push bp
	mov bp,sp
	mov ax,BaseOfKernel
	mov es,ax
	mov bx,OffsetOfKernel
	add bx,word[index]
	xor ax,ax
	mov ax,[bp+4] ;读取扇区数
	add ax,RootDirSectors	;算数扇区数
	mov byte[BLOCK],1 ;只读取一扇区
	push ax
	call LABEL_READ
	add sp,2
	pop bp
	add word[index],512
	ret
LABEL_CMP:
	push bp
	mov  bp,sp
	xor  si,si
	xor  cx,cx
	mov si,FileName		;ds:si=文件指针偏移
	mov cx,word[bp+4]		;cx=文件名大小
	xor edi,edi
	mov di,bx
	cld
.cmp:
	lodsb
	cmp al,byte[es:di]
	jnz .1
	inc di
	loop .cmp
	mov ax,0		;找到返回0
	pop bp	
	ret
.1:
	mov ax,1		;没有找到返回1
	pop bp
	ret
	
	
LABEL_READ:
;扇区号 x
	;                |商y 柱面号y>>1 磁头号y&1  
	; x/每磁道扇区数=| 
	;                |余数z 起始扇区号z+1
	push bp
	mov bp,sp
.1:	xor eax,eax
	mov ax,[bp+4]  		;扇区号
	xor dx,dx
	push bx
	mov bx,SecPerTrk 	;每磁道扇区数
	div bx  		;商y=ax,余数z=dx
	push ax
	shr al,1		;柱面号=y>>1
	mov ch, al		;ch=al>>1
	pop ax
	and al,1		;磁头号=y&1
	mov dh,al		;dh=y&1
	add dl,1		;起始扇区号=z+1
	mov cl,dl		;cl=z+1
	mov al,byte[BLOCK]	;读取扇区数
	mov ah,2		;BIOS 中断ah=2
	pop bx
	mov dl,0		;dl=驱动器号
	int 13h			;BOIS 中断号
	cmp ah,0		;读成功ah=0
	jnz .1
	pop bp
	ret	
[section .s32]
[bits 32]
START:
	jmp	$
	nop
	nop
	xor eax,eax
	mov ax,16+8
	mov gs,ax
	mov edi,(50*12+12)*2
	mov ah,'K'
	mov al,0ch
	mov [gs:edi],ax
	jmp $
	
	
